package com.xpqh.ksg.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xpqh.ksg.common.cache.ServiceApiCache;
import com.xpqh.ksg.common.enums.result.BizExceptionEnum;
import com.xpqh.ksg.common.persistence.model.KsgServiceApi;
import com.xpqh.ksg.common.utils.JwtTokenUtil;
import com.xpqh.ksg.common.utils.RenderUtil;
import com.xpqh.ksg.common.utils.ResultUtil;
import com.xpqh.ksg.common.wrapper.DecryptedRequestWrapper;
import com.xpqh.ksg.config.properties.JwtProperties;
import common.utils.AESUtil;
import common.utils.MD5Util;
import common.utils.StringUtils;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 安全校验
 * @author lizhihui
 * date  2025.03.22
 */
@Slf4j
public class AuthFilter extends OncePerRequestFilter {

	@Autowired
	private JwtTokenUtil jwtTokenUtil;

	@Autowired
	private JwtProperties jwtProperties;

	@Autowired
	private ServiceApiCache serviceApiCache;

	private final ObjectMapper objectMapper = new ObjectMapper();

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
		if (request.getMethod().equals("OPTIONS")) {
			RenderUtil.renderJson(response, ResultUtil.OK());
			return;
		}
		if (request.getServletPath().contains("swagger") || request.getServletPath().contains("ksg-public")) {
			chain.doFilter(request, response);
			return;
		}
		String serviceUri = request.getServletPath();
		if (serviceUri.contains("/ksg/sys/area/children") || serviceUri.contains("/ksg/sys/area/detail")) {
			// 截取0-最后一个/的数据，不包含最后一个斜杠
			serviceUri = serviceUri.substring(0, serviceUri.lastIndexOf("/"));
		}
		KsgServiceApi api = serviceApiCache.getKsgServiceApi(serviceUri);
		request.setAttribute("apiObject", api);

		if (null == api || api.getIsAccess() == 0) {
			log.info("非法请求不存在的接口:{}", request.getServletPath());
			RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.API_ERROR));
			return;
		}
		DecryptedRequestWrapper decryptedRequestWrapper = null;
		// token校验
		if (api.getIsTokenValidate() == 1) {
			String requestHeader = request.getHeader(jwtProperties.getHeader());
			if (!StringUtils.isNotBlank(requestHeader) || !requestHeader.startsWith("Bearer ")) {
				RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.EYI));
				return;
			}

			String authToken = requestHeader.substring(7);

			String userId = "";

			// 登录校验
			if (api.getIsLogin() == 1 && jwtProperties.getDefaultToken().equals(authToken)) {
				RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.ACCOUNT_IS_NOT_LOGIN));
				return;
			}

			try {
				// 校验token是否有效
				if (jwtTokenUtil.isTokenExpired(authToken)) {
					userId = jwtTokenUtil.getUsernameFromToken(authToken);
					log.info("{} token eExpired, userId:{}, token:{}", request.getServletPath(), userId, authToken);
					RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.TOKEN_EXPIRED));
					return;
				}
			} catch (JwtException e) {
				//有异常就是token解析失败
				log.error("{}接口解析token异常:{}", api.getServiceName(), e.getMessage());
				RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.TOKEN_ERROR));
				return;
			}

			// 时间戳校验
			if (!validateTimeStamp(request.getHeader("Client-Time"))) {
				RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.EYI));
				return;
			}


			// 签名校验
			if (api.getIsSginValidate() == 1) {
				try {
					String requestBody = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))
							.lines()
							.collect(Collectors.joining("\n"));
					if (StringUtils.isNotBlank(requestBody)) {
						// 解析 JSON 请求体
						Map<String, String> requestMap = objectMapper.readValue(requestBody, Map.class);
						// 获取 object 和 sign 参数
						String object = requestMap.get("object");
						String sign = requestMap.get("sign");

						// 校验签名
						if (!isValidSignature(object, sign, authToken)) {
							log.warn("{} SIGN VERIFICATION FAILED!, userId:{}, msg:{}", request.getServletPath(), userId, object);
							RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.SIGN_ERROR));
							return;
						}

						String md5KeyFromToken = jwtTokenUtil.getMd5KeyFromToken(authToken);
						String params = AESUtil.decrypt(object, md5KeyFromToken);

						// 将解密后的数据重新封装为 HttpServletRequest
						decryptedRequestWrapper = new DecryptedRequestWrapper(request, params);
					}
				} catch (Exception e) {
					log.error("{}签名校验异常:{}", api.getServiceName(), e.getMessage());
					RenderUtil.renderJson(response, ResultUtil.ERROR(BizExceptionEnum.SIGN_ERROR));
					return;
				}
			}
		}
		if (decryptedRequestWrapper != null) {
			chain.doFilter(decryptedRequestWrapper, response);
		} else {
			chain.doFilter(request, response);
		}
	}

	/**
	 * 签名校验
	 * @return boolean
	 */
	private boolean isValidSignature(String object, String sign, String authToken) {
		String md5KeyFromToken = jwtTokenUtil.getMd5KeyFromToken(authToken);
		md5KeyFromToken = new StringBuffer(md5KeyFromToken).reverse().toString();
		String encrypt = MD5Util.encrypt(object + md5KeyFromToken + jwtProperties.getRandomKey());
		return encrypt.equals(sign);
	}

	/**
	 * 判断客户端的北京时间和服务器时间是否超过900秒
	 * @return boolean
	 */
	private boolean validateTimeStamp(String clientTime) {
		if (!StringUtils.isNotBlank(clientTime)) {
			return false;
		}
		long absDiff = Math.abs(System.currentTimeMillis() / 1000L - Long.parseLong(clientTime));
		return absDiff <= 900;
	}
}
